//Global variables
var cultureInfoString; //variable for storing culture info string (e.g. en-EN, sv-SE, etc.)

/******************************************************************************
 * Common functions
 *****************************************************************************/

	// Opens a folder in the tfCBTwoTier control.
	function tfCBTwoTier_OpenFolder( id ) {
		var div1 = xGetElementById( id + "-close" );
		var div2 = xGetElementById( id + "-open" );

		if ( div1 != null && div2 != null ) {
			div1.style.display = "none";
			div2.style.display = "";
		}
	}
	
	// Closes a folder in the tfCBTwoTier control.
 	function tfCBTwoTier_CloseFolder( id ) {
		var div1 = xGetElementById( id + "-open" );
		var div2 = xGetElementById( id + "-close" );

		if ( div1 != null && div2 != null ) {
			div1.style.display = "none";
			div2.style.display = "";
		}
	}
	
	// Set category highlight
	function tfCBTwoTier_Highlight( id , categoryHighlight ) {
		var div1 = xGetElementById( id + "-close" );
		var div2 = xGetElementById( id + "-expandTitle" );
        var highlightColor = "#FFFBC6";
        
		if ( div1 != null && div2 != null ) {
		    if (categoryHighlight)
		    {
			    div1.style.backgroundColor = highlightColor;
			    div2.style.backgroundColor = highlightColor;
			}
			else
			{
			    div1.style.backgroundColor = "";
			    div2.style.backgroundColor = "";
			}
		}
	}
	
	// Set the visibility of second category chosen alert
	function tfCBTwoTier_SecondCategoryAlert( id , moreThanTwo) {
		var div = xGetElementById( "alertSecondCategory_" + id );
		if (div != null)
		{
		    if (moreThanTwo)
		    {
		        div.style.display = "block";
		    }
		    else
		    {
		        div.style.display = "none";
		    }
		}
	}
	
	// Writes a specified text to the "debug" DIV tag:
 	function tfCBTwoTier_Debug( text ) {
		var div = document.getElementById( "debug" );
		if ( div != null ) div.innerHTML += text + "<br/>";
	}
	
	// custom string comparison function to handle unicode characters, returns str1 > str2 basically
	function strCompare_UniCode(str1, str2) {
		var minLength;
		str1 = str1.toLowerCase();
		str2 = str2.toLowerCase();
		if (str1.length < str2.length) minLength = str1.length; else minLength = str2.length;		 
		for (var i=0;i<minLength-1;i++)
		{		
			if (str1.charCodeAt(i) == str2.charCodeAt(i)) continue;
			return(mapUnicodeChar(str1.charCodeAt(i)) > mapUnicodeChar(str2.charCodeAt(i)));
		}		
		return (str1.length > str2.length);
	}
	
	function mapUnicodeChar(chrCode)
	{
	  var retval;
	  	  
		switch(chrCode)
		{	
			case 224: retval = 97.1; break;
			case 225: retval = 97.2; break;
			case 226: retval = 97.3; break;
			case 228: retval = 97.4; break;
			case 229: retval = 97.5; break;
			case 230: retval = 97.7; break;
			case 231: retval = 99.5; break;
			case 232: retval = 100.3; break;
			case 233: retval = 100.5; break;
			case 234: retval = 100.7; break;
			case 244: retval = 110.3; break;
			case 246: retval = 110.5; break;
			case 214: retval = 110.5; break;
			case 252: retval = 117.5; break;
			default:  retval = chrCode; break;
		}
		if (cultureInfoString == "sv-se" || cultureInfoString == "fi-fi")
		{
			switch(chrCode)
			{
				case 229: retval = 122.2; break;
				case 228: retval = 122.4; break;
				case 246: retval = 122.6; break;
			}
		}
		else if (cultureInfoString == "nb-no" || cultureInfoString == "da-dk")
		{
			switch(chrCode)
			{
				case 230: retval = 122.2; break;
				case 248: retval = 122.4; break;
				case 229: retval = 122.6; break;
			}
		}
		return retval;
	}
	
	// Adds a new event handler to the specified element:
	function tfCBTwoTier_AddEvent( element, eventName, eventHandler ) {
		if ( element.attachEvent != null ) {
			element.attachEvent( 'on' + eventName, eventHandler );
		}
		
		else if ( element.addEventListener != null ) {
			element.addEventListener( eventName, eventHandler, false );
		}
	
		else {
			if ( element[ eventName + "Handlers" ] == null ) {
				element[ eventName + "Handlers" ] = [];
				
				if ( element[ "on" + eventName ] != null ) {
					element[ eventName + "Handlers" ].push( element[ "on" + eventName ] );
				}
	
				element[ "on" + eventName ] = new Function( "e", "for ( var i = 0; i < this." + eventName + "Handlers.length; i++ ) this." + eventName + "Handlers[ i ]( e );" );
			}
					
			element[ eventName + "Handlers" ].push( eventHandler );
		}
	}
	
	
	// Returns the absolute offset top, meaning the offset to the top of the element
	// located at (0, 0). This is computed as the recursive sum of the offsetTop
	// property of all elements, which are ancestors of the given element, with respect 
	// to offsetParent properties stored by the browser. The result of this sum is
	// browser independent, although each browser implements different offsetParent logic
	// (e.g. either relative to the closest enclosing relative element or to the page)
	function getAbsoluteOffsetTop(id) {
		var result = 0;

		var ancestor = xGetElementById(id);
		while (ancestor != null) {
			result += ancestor.offsetTop;
			ancestor = ancestor.offsetParent;
		}
		
		return result;
	}
	

/******************************************************************************
 * tfCBTwoTier
 *****************************************************************************/

	/*
	Creates a new instance of the tfCBTwoTier class.
	folders ... Array of folders to add to the tfCBTwoTier class.
	items ... Array of top-level items to add to the tfCBTwoTier class.
	Each element in the "folders" array is an two-element array, containing folder name and
	array of folder items. Each element in the "items" array is a two element array containing
	item value and item name. Arrays were chosen to reduce the amount of Javascript code
	which is sent from server to initialize the tfCBTwoTier on client side.
	*/ 
	function tfCBTwoTier( name, folders, items ) {
		this.name = name;
		this.folders  = [];
		this.items    = [];
		this.validators = [];
		this.changeEventHandlers = [];
		this.errorEventHandlers = [];
		this.itemsDictionary = null;
		this.applyAlertSecondCatSelected = false;
		this.highlightSelectedItem = false;
		if ( window[ name ] == null ) window[ name ] = this;
		for ( var i = 0; i < folders.length; i++ ) if ( folders[ i ] != null ) this.folders.push( new tfCBTwoTier_Folder( this.name, this.nextFolderID(), folders[ i ] ) );
		for ( var i = 0; i < items.length; i++ ) if ( items[ i ] != null ) this.items.push( new tfCBTwoTier_Item( this.name, null, this.nextItemID(), items[ i ] ) );
	}

	tfCBTwoTier.prototype.nextFolderID = function() {
		if ( this.folderCnt == null ) this.folderCnt = 0;
		return this.name + "-" + this.folderCnt++;
	}

	tfCBTwoTier.prototype.nextItemID = function() {
		if ( this.itemCnt == null ) this.itemCnt = 0;
		return this.name + "--" + this.itemCnt++;
	}
	
	// Renders the contect of the tfCBTwoTier control into a element with a specified ID.
	tfCBTwoTier.prototype.render = function( divID ) {
		if ( divID != null ) {
			this.divID = divID;
		}

		if ( this.divID == null ) return;
		
		var html = [];

		// Render only items in this specific folder:
		if ( this.folderToRender != null ) {
			for ( var i = 0; i < this.folderToRender.items.length; i++ ) this.renderItem( this.folderToRender.items[ i ], html );	
		}

		// Render all folders and items:
		else {
			if ( this.renderItemsFirst ) {
				for ( var i = 0; i < this.items.length; i++ ) this.renderItem( this.items[ i ], html );	
				for ( var i = 0; i < this.folders.length; i++ ) this.renderFolder( this.folders[ i ], html );	
			}
                
			else {
				for ( var i = 0; i < this.folders.length; i++ ) this.renderFolder( this.folders[ i ], html );	
				for ( var i = 0; i < this.items.length; i++ ) this.renderItem( this.items[ i ], html );	
			}
		}

		this.element = document.getElementById( this.divID );
		this.element.innerHTML = html.join( "" );
		this.attachEvents();
	}

	// Iterate through all folders and execute a specified callback function on each of them: 
	tfCBTwoTier.prototype.iterateFolders = function( callback, result ) {
		for ( var i = 0; i < this.folders.length; i++ ) {
			if ( callback( this.folders[ i ], result ) ) return;
		}
	}

	// Iterate through all items and execute a specified callback function on each of them: 
	tfCBTwoTier.prototype.iterateItems = function( callback, result ) {
		for ( var i = 0; i < this.folders.length; i++ ) {
			var folder = this.folders[ i ];
			
			for ( var j = 0; j < folder.items.length; j++ ) {
				if ( callback( folder.items[ j ], result ) ) return;
			}
		}

		for ( var j = 0; j < this.items.length; j++ ) {
			if ( callback( this.items[ j ], result ) ) return;
		}
	}

	// Gets the Item with a specified value: 
	tfCBTwoTier.prototype.getItem = function( value ) {
		if ( this.itemsDictionary == null ) {
			var callback = function( item, result ) { result[ item.value ] = item; }
			this.itemsDictionary = new Object();
			this.iterateItems( callback, this.itemsDictionary );
		}

		return this.itemsDictionary[ value ];
	}
	
	// Gets the Folder with a specified name: 
	tfCBTwoTier.prototype.getFolderByName = function( name ) {
		if ( this.foldersDictionary == null ) {
			var callback = function( folder, result ) { result[ folder.name ] = folder; }
			this.foldersDictionary = new Object();
			this.iterateFolders( callback, this.foldersDictionary );
		}

		return this.foldersDictionary[ name ];
	}
	
	// Selects (checks) a specified item.
	tfCBTwoTier.prototype.select = function( value, checked ) {
		var item = this.getItem( value );
		if ( item != null ) item.select( checked );
	}
	
	// Focuses a specified item.
	tfCBTwoTier.prototype.focus = function( value ) {
		var item = this.getItem( value );

		if ( item != null ) {
			this.focusLastItem();
			if ( item.folder != null ) item.folder.open();
			item.focus(); 
		}
	}

	// Focuses the last item.
	tfCBTwoTier.prototype.focusLastItem = function() {
		if ( this.lastItem != null ) {
			var openFolder = ( this.lastItem.folder != null && !this.lastItem.folder.isOpened() );
			var enabled = this.lastItem.isEnabled();
			if ( openFolder ) this.lastItem.folder.open();
			if ( !enabled ) this.lastItem.enable();
			this.lastItem.focus();
			if ( !enabled ) this.lastItem.disable();
			if ( openFolder ) this.lastItem.folder.close();
		}
	}

	// Focuses the first selected item.
	tfCBTwoTier.prototype.focusFirstSelectedItem = function() {
		if ( this.getSelection().itemCount > 0 ) {
			var firstItem;

			if ( this.renderItemsFirst ) {
				if ( this.getSelection().items.length > 0 ) {
					firstItem = this.getSelection().items[ 0 ];
				}

				else {
					firstItem = this.getSelection().folders[ 0 ].items[ 0 ];
				}
			}

			else {
				if ( this.getSelection().folders.length > 0 ) {
					firstItem = this.getSelection().folders[ 0 ].items[ 0 ];
				}

				else {
					firstItem = this.getSelection().items[ 0 ];
				}
			}

			this.focusLastItem();
			if ( firstItem.folder != null ) firstItem.folder.open();
			firstItem.focus(); 
		}
	}
	
	// Returns the list of all selected items organized into folders.
	tfCBTwoTier.prototype.getSelection = function() {
		if ( this.selection == null ) {
			this.selection = new tfCBTwoTier_Selection();
		    var moreThanTwo = false;
		    var countCategoryChosen = 0;
		    
			// Get selected items from the folders:
			for ( var i = 0; i < this.folders.length; i++ ) {
				var folder = this.folders[ i ];
				var selectedFolder = null;
				var itemsCheck = false;
				var hasItemSelected = false;
				folder.selectedItemsCount = 0;
				
				for ( var j = 0; j < folder.items.length; j++ ) {
					var item = folder.items[ j ];
					
					if ( item.isSelected() ) {
						if ( selectedFolder == null ) {
							selectedFolder = new tfCBTwoTier_Folder( this.name, folder.id, [ folder.name, [], folder.folderInfo ] );
							this.selection.folders.push( selectedFolder );
						}
						
						itemsCheck = true;
						selectedFolder.items.push( item );
						folder.selectedItemsCount++;
						this.selection.itemCount++;
						hasItemSelected = true;
					}
				}
						
				if (this.applyAlertSecondCatSelected && hasItemSelected)
				    countCategoryChosen += 1;
				
				//Decide the highlight of category
				if (this.highlightSelectedItem)
				    tfCBTwoTier_Highlight(folder.id, itemsCheck);
			}

			// Get top-level selected items:
			for ( var i = 0; i < this.items.length; i++ ) {
				var item = this.items[ i ];
				if ( item.isSelected() ) {
					this.selection.items.push( item );
					this.selection.itemCount++;
				}
			}
			
			if(this.applyAlertSecondCatSelected)
			{
			    if (countCategoryChosen > 1)
			    {
			        moreThanTwo = true;
			    }
    		    //Decide the visibility of second category alert 
			    tfCBTwoTier_SecondCategoryAlert(this.name, moreThanTwo);
			}
		}
		return this.selection;
	}
	// Flag to indicate whether any items are selected irrespective of folder
	tfCBTwoTier.prototype.isSelected = function() {
		// Get selected items from the folders:
		for ( var i = 0; i < this.folders.length; i++ ) {
			var folder = this.folders[ i ];
			for ( var j = 0; j < folder.items.length; j++ ) {
				if (folder.items[ j ].isSelected()) return true;			
			}
		}
		// Get top-level selected items:
		for ( var i = 0; i < this.items.length; i++ ) {
			if (this.items[i].isSelected()) return true;
		}				
		return false;
	}
	
	// Gets the comma-separated list of selected values:
	tfCBTwoTier.prototype.getValue = function() {
		if ( this.value == null ) {
			var selectedValues = [];
			
			for ( var i = 0; i < this.getSelection().folders.length; i++ ) {
				for ( var j = 0; j < this.getSelection().folders[ i ].items.length; j++ ) {
					selectedValues.push( this.getSelection().folders[ i ].items[ j ].value );
				}
			}
			
			for ( var i = 0; i < this.getSelection().items.length; i++ ) {
				selectedValues.push( this.getSelection().items[ i ].value );
			}
			
			this.value = selectedValues.join( "," )
		}
		
		return this.value;
	}
	
	// Attaches event handlers.
	tfCBTwoTier.prototype.attachEvents = function() {
		if ( !this.disabled ) {
			var checkBoxes = document.forms[ this.formID ].elements[ this.name + "_values" ];
			var k = 0;
			var checkBox;
			
			var onClick = function() {
				var checkBoxList = window[ this.checkBoxListName ];
				checkBoxList.selection = null;
				checkBoxList.value = null;
				if ( checkBoxList.validate() ) checkBoxList.onChange( this.getItem() );
				else this.checked = false;
			}

			if ( this.folderToRender != null ) {
				var getItem = function() { return window[ this.checkBoxListName ].folderToRender.items[ this.itemIndex ]; }
				
				for ( var j = 0; j < this.folderToRender.items.length; j++ ) {
					checkBox = ( checkBoxes.length != null ) ? checkBoxes[ k++ ] : checkBoxes;
					this.folderToRender.items[ j ].checkBox = checkBox;
					checkBox.itemIndex = j;
					checkBox.checkBoxListName = this.name
					checkBox.getItem = getItem;
					checkBox.onclick = onClick;
				}
			}

			else {
				var getItem1 = function() { return window[ this.checkBoxListName ].folders[ this.folderIndex ].items[ this.itemIndex ]; }
				var getItem2 = function() { return window[ this.checkBoxListName ].items[ this.itemIndex ]; }

				if ( this.renderItemsFirst ) {
					for ( var j = 0; j < this.items.length; j++ ) {
						checkBox = ( checkBoxes.length != null ) ? checkBoxes[ k++ ] : checkBoxes;
						this.items[ j ].checkBox = checkBox;
						checkBox.itemIndex = j;
						checkBox.checkBoxListName = this.name
						checkBox.getItem = getItem2;
						checkBox.onclick = onClick;
					}
				}
                        
				for ( var i = 0; i < this.folders.length; i++ ) {
					var folder = this.folders[ i ];
					
					for ( var j = 0; j < folder.items.length; j++ ) {
						checkBox = ( checkBoxes.length != null ) ? checkBoxes[ k++ ] : checkBoxes;
						folder.items[ j ].checkBox = checkBox;
						checkBox.folderIndex = i;
						checkBox.itemIndex = j;
						checkBox.checkBoxListName = this.name
						checkBox.getItem = getItem1;
						checkBox.onclick = onClick;
					}
				}
                        
				if ( !this.renderItemsFirst ) {
					for ( var j = 0; j < this.items.length; j++ ) {
						checkBox = ( checkBoxes.length != null ) ? checkBoxes[ k++ ] : checkBoxes;
						this.items[ j ].checkBox = checkBox;
						checkBox.itemIndex = j;
						checkBox.checkBoxListName = this.name
						checkBox.getItem = getItem2;
						checkBox.onclick = onClick;
					}
				}
			}
		}
	}

	// Adds the specified folders to the checkboxlist. The folders and items in the CheckBoxList 
	// should be ordered by name.
	tfCBTwoTier.prototype.addFolders = function( folders ) {
		var modified = false;
		if (folders == undefined) return false;
		for ( var i = 0; i < folders.length; i++ ) {
			if ( folders[ i ] == null ) continue;
 
			// The name of the folder to insert:
			var folderName = folders[ i ][ 0 ];

			// The items in the folder to insert:
			var items = folders[ i ][ 1 ];

			// The existing or newly created folder:
			var folder = null;
			
			for ( var ii = 0; ii < this.folders.length; ii++ ) {
				// The specified folder already exists in the control, therefore we will
				// add the items to this existing folder. 
				if ( this.folders[ ii ].name.toLowerCase() == folderName.toLowerCase() ) {
					folder = this.folders[ ii ];
					
					// Add items that do not exist in the folder:
					for ( var j = 0; j < items.length; j++ ) {
						if ( items[ j ] == null ) continue;

						// The name of the item to insert:
						var itemName  = items[ j ][ 1 ];

						// The existing or newly created item:
						var item = null;
						
						for ( var jj = 0; jj < folder.items.length; jj++ ) {
							// If the item already exists in the folder, then do nothing.
							if ( folder.items[ jj ].name.toLowerCase() == itemName.toLowerCase() ) {
								item = folder.items[ jj ];
								break;
							}

							// Otherwise insert the new item to the folder.
							// We need to preserve the alphabetical order of the item names.							
							if ( this.sortItems && strCompare_UniCode( folder.items[ jj ].name, itemName ) ) {
								item = new tfCBTwoTier_Item( this.name, folder, folder.nextItemID(), items[ j ] );
								folder.items.splice( jj, 0, item );
								modified = true;
								break;
							}
						}
							
						if ( item == null ) {
							item = new tfCBTwoTier_Item( this.name, folder, folder.nextItemID(), items[ j ] );
							folder.items.push( item );
							modified = true;
						}
					}
					
					break;
				}
				
				// The specified folder does not exist in the checkbox list, therefore
				// we will insert the new folder with all its items. We need to preserve the alphabetical
				// order of the folder names.
				if ( this.sortFolders && strCompare_UniCode( this.folders[ ii ].name, folderName ) ) {
					folder = new tfCBTwoTier_Folder( this.name, this.nextFolderID(), folders[ i ] );
					this.folders.splice( ii, 0, folder );
					modified = true;
					break;
				}
			}

			if ( folder == null ) {
				folder = new tfCBTwoTier_Folder( this.name, this.nextFolderID(), folders[ i ] );
				this.folders.push( folder );
				modified = true;
			}
		}

		if ( modified ) this.itemsDictionary = null;
		return modified;
	}

	// Adds the specified folders to the checkboxlist. The folders and items in the CheckBoxList 
	// should be ordered by name.
	tfCBTwoTier.prototype.addFoldersOptimized = function( folders ) {
		var modified = false;
		if (folders == undefined) return false;
		for ( var i = 0; i < folders.length; i++ ) {
			if ( folders[ i ] == null ) continue;
 
			// The name of the folder to insert:
			var folderName = folders[ i ][ 0 ];

			// The items in the folder to insert:
			var items = folders[ i ][ 1 ];

			// The existing or newly created folder:
			var folder = null;

			folder = this.getFolderByName(folderName);
			
			if (folder != null) {
				// Add items that do not exist in the folder:
				for ( var j = 0; j < items.length; j++ ) {
					if ( items[ j ] == null ) continue;

					// The value and the name of the item to insert:
					var itemValue = items[ j ][ 0 ]
					var itemName  = items[ j ][ 1 ];

					// The existing or newly created item:
					var item = null;
					
					// Try to get the item from the cache
					item = this.getItem(itemValue);
					
					// If the item already exists, there's nothing to do
					if (item == null) {
						// Otherwise insert the new item to the folder.
						item = new tfCBTwoTier_Item( this.name, folder, folder.nextItemID(), items[ j ] );
						// update the item cache
						this.itemsDictionary[itemValue] = item;
						modified = true;

						var indexForNewItem = -1;
						if (this.sortItems) {
							// begining of the search of the insert index with binary search
							var low = 0;
							var high = folder.items.length - 1;
							
							while (low != high) {
								var median = (low + high) >>> 1; // bug-free and efficient parseInt((low + high) / 2);
								var medianItemName = folder.items[median].name;
								if (strCompare_UniCode(itemName, medianItemName)) {
									// search the upper half
									low = median + 1;
								}
								else {
									// search through the lower half
									high = median;
								}
							}
							
							// if value is greater then the ith, we'll insert it to the right
							// this may only happen at the last index of the array
							if (low == folder.items.length - 1 && strCompare_UniCode(itemName, folder.items[low].name)) {
								indexForNewItem = low + 1;
							}
							else {
								// by convention, if the value are equal, always insert before
								indexForNewItem = low;
							}
							// end of the search of the insert index with binary search
						}

						if (indexForNewItem != -1 && indexForNewItem < folder.items.length) {
							// The specified folder does not exist in the checkbox list, therefore
							// we will insert the new folder with all its items. We need to preserve the alphabetical
							// order of the folder names.
							// We need to preserve the alphabetical order of the item names.							
							folder.items.splice( indexForNewItem, 0, item );
						}
						else {
							folder.items.push( item );
						}
					}
				}
			}
			else {
				// this is a very rare case and it doesn't matter if in this case the function is not optimal
				// (i.e. it iterates over the folder list
				folder = new tfCBTwoTier_Folder( this.name, this.nextFolderID(), folders[ i ] );

				var indexForNewFolder = -1;
				
				if (this.sortFolders) {
					for (var ii = 0; ii < this.folders.length; ii++) {
						if (strCompare_UniCode(this.folders[ii].name, folderName)) {
							indexForNewFolder = ii;
							break;
						}
					}	
				}

				if (indexForNewFolder != -1) {
					// The specified folder does not exist in the checkbox list, therefore
					// we will insert the new folder with all its items. We need to preserve the alphabetical
					// order of the folder names.
					this.folders.splice( this.folders.length, 0, folder );
				}
				else {
					this.folders.push(folder);
				}
								
				modified = true;
				// update the folder cache
				this.foldersDictionary[folderName] = folder;
			}
		}

		return modified;
	}

	// Adds the specified top-level items to the checkboxlist:
	tfCBTwoTier.prototype.addItems = function( items ) {
		var modified = false;
		if (items == undefined) return false;
		for ( var j = 0; j < items.length; j++ ) {
			if ( items[ j ] == null ) continue;

			// The name of the item to insert:
			var itemName  = items[ j ][ 1 ];

			// The existing or newly created item:
			var item = null;
			
			for ( var jj = 0; jj < this.items.length; jj++ ) {
				// If the item already exists in the checkbox list, then do nothing.
				if ( this.items[ jj ].name.toLowerCase() == itemName.toLowerCase() ) {
					item = this.items[ jj ];
					break;
				}

				// Otherwise insert the new item to the checkbox list.
				// We need to preserve the alphabetical order of the item names.
				if ( this.sortItems && strCompare_UniCode( this.items[ jj ].name, itemName ) ) {
					item = new tfCBTwoTier_Item( this.name, null, this.nextItemID(), items[ j ] );
					this.items.splice( jj, 0, item );
					modified = true;
					break;
				}
			}
							
			if ( item == null ) {
				item = new tfCBTwoTier_Item( this.name, null, this.nextItemID(), items[ j ] );
				this.items.push( item );
				modified = true;
			}
		}

		if ( modified ) this.itemsDictionary = null;
		return modified;
	}

	// Removes folders from the checkboxlist:
	tfCBTwoTier.prototype.removeFolders = function( folders ) {
		var modified = false;
		var selectionChanged = false;
		if (folders == undefined) return false;
		for ( var i = 0; i < folders.length; i++ ) {
			// The name of the folder to remove:
			var folderName = folders[ i ][ 0 ];

			// The items in the folder to remove:
			var items = folders[ i ][ 1 ];
			
			for ( var ii = 0; ii < this.folders.length; ii++ ) {
				// The folders are ordered by name, so we do not need to scan all folders:				
				if ( this.sortFolders && strCompare_UniCode( this.folders[ ii ].name, folderName ) ) break;
				
				// If the specified folder exists in the control, we will
				// remove the items to this existing folder. 
				if ( this.folders[ ii ].name.toLowerCase() == folderName.toLowerCase() ) {
					var folder = this.folders[ ii ];
					
					for ( var j = 0; j < items.length; j++ ) {
						if (items[ j ]== undefined) break;
						// The name of the item to remove:
						var itemName  = items[ j ][ 1 ];
						
						for ( var jj = 0; jj < folder.items.length; jj++ ) {
							// The items are ordered by name so we do not need to scan all items.							
							if ( this.sortItems && strCompare_UniCode( folder.items[ jj ].name, itemName ) ) break;

							// If we found the item, we will remove it from the folder.
							if ( folder.items[ jj ].name.toLowerCase() == itemName.toLowerCase() ) {
								selectionChanged = selectionChanged || folder.items[ jj ].isSelected();
								folder.items[ jj ].folder = null;
								folder.items.splice( jj, 1 );
								modified = true;
								break;
							}
						}
					}

					// If there are no more items left in the folder, then remove the folder itself:
					if ( folder.items.length == 0 ) {
						this.folders.splice( ii, 1 );
						modified = true;
					}
					
					break;
				}
			}
		}

		if ( selectionChanged ) {
			this.selection = null;
			this.value = null;
			this.onChange();
		}
		
		if ( modified ) this.itemsDictionary = null;
		return modified;
	}

	// Removes items from the checkboxlist:
	tfCBTwoTier.prototype.removeItems = function( items ) {
		var modified = false;
		var selectionChanged = false;
		if (items == undefined) return false;
		for ( var j = 0; j < items.length; j++ ) {
			// The name of the item to remove:
			var itemName  = items[ j ][ 1 ];
			
			for ( var jj = 0; jj < this.items.length; jj++ ) {
				// The items are ordered by name so we do not need to scan all items.
				if ( this.sortItems && strCompare_UniCode( this.items[ jj ].name, itemName ) ) break;

				// If we found the item, we will remove it from the checkboxlist.
				if ( this.items[ jj ].name.toLowerCase() == itemName.toLowerCase() ) {
					selectionChanged = selectionChanged || this.items[ jj ].isSelected();
					this.items.splice( jj, 1 );
					modified = true;
					break;
				}
			}
		}

		if ( selectionChanged ) {
			this.selection = null;
			this.value = null;
			this.onChange();
		}

		if ( modified ) this.itemsDictionary = null;
		return modified;
	}

	// Removes all folders and items from the checkboxlist:
	tfCBTwoTier.prototype.clear = function() {
		// Remove circular references
		for ( var i = 0; i < this.folders.length; i++ ) {
			for ( var j = 0; j < this.folders[ i ].items.length; j++ ) {
				this.folders[ i ].items[ j ].folder = null;
			}
		}

		this.itemsDictionary = null;
		this.folders = [];
		this.items = [];
	}

	// Unselects all selected items:
	tfCBTwoTier.prototype.clearSelection = function() {
		var selectedValues = this.getValue().split( /,/g );
		for ( var i = 0; i < selectedValues.length; i++ ) this.select( selectedValues[ i ], false );
	}

	// Adds a new validator.
 	tfCBTwoTier.prototype.addValidator = function( validator ) {
		this.validators.push( validator );
	}

	// Validates the current state of the checkBoxList.
	tfCBTwoTier.prototype.validate = function() {
		for ( var i = 0; i < this.validators.length; i++ ) {
			if ( !this.validators[ i ].validate( this ) ) {
				this.onError( this.validators[ i ].errorMessage );
				this.selection = null;
				return false;
			}
		}
		
		return true;
	}

	// Adds a new event handler for the "Change" event.
 	tfCBTwoTier.prototype.addChangeEventHandler = function( eventHandler ) {
		this.changeEventHandlers.push( eventHandler );
	}

	// Raises the "Change" event.
	tfCBTwoTier.prototype.onChange = function( item ) {
		for ( var i = 0; i < this.changeEventHandlers.length; i++ ) {
			var handler = this.changeEventHandlers[ i ];

			if ( typeof( handler ) == "object" ) {
				handler.execute( this, item );
			}

			else {
				handler( this, item );
			}
		}
	}

	// Adds a new event handler for the "Error" event.
 	tfCBTwoTier.prototype.addErrorEventHandler = function( eventHandler ) {
		this.errorEventHandlers.push( eventHandler );
	}

	// Raises the "Error" event.
	tfCBTwoTier.prototype.onError = function( errorMessage ) {
		if ( this.errorEventHandlers.length > 0 ) {
			for ( var i = 0; i < this.errorEventHandlers.length; i++ ) {
				this.errorEventHandlers[ i ]( this, errorMessage );
			}
		}
		
		else {
			alert( errorMessage );
		}
	}
	
	// Handles the event when the item is programmatically selected:
	tfCBTwoTier.prototype.onItemSelected = function( item ) {
		this.selection = null;
		this.value = null;
		
		if ( this.validate() ) {
			this.onChange( item );
					
			if ( item.isSelected() && this.openFolderOnSelect && item.folder != null ) {
				item.folder.open();
				//item.focus();
			}
					
			return true;
		}
		
		return false;
	}
	
	// Specifies whether the control is disabled:
	tfCBTwoTier.prototype.disabled = false;

	// Sets the "disabled" property to a specified value.
	tfCBTwoTier.prototype.setDisabled = function( value ) {
		if ( this.disabled != value ) {
			this.disabled = ( value == true );
			if ( this.disabled ) this.clearSelection();
			this.render();
		}
	}

	// Defines the layout of the folder in the tfCBTwoTier control.
    tfCBTwoTier.prototype.folderFormatString = "<div id=\"{Folder.ID}-close\" class=\"folder\"  onClick=\"tfCBTwoTier_OpenFolder('{Folder.ID}');\"><div class=\"expand\"><code>+</code></div><div id=\"{Folder.ID}-collapseTitle\" class=\"cbl_folder\">{Folder.Name}</div></div>\n<div id=\"{Folder.ID}-open\" class=\"folder\" style=\"display:none;\"><div onClick=\"tfCBTwoTier_CloseFolder('{Folder.ID}');\"><div class=\"collapse\"><code>-</code></div><div id=\"{Folder.ID}-expandTitle\" class=\"cbl_item\">{Folder.Name}</div></div>{Folder.Items}</div>";

	// Defined the layout of the item in the tfCBTwoTier control.
	tfCBTwoTier.prototype.itemFormatString = "<div class=\"item\">{Checkbox}<div class=\"cbl_item\"><label for=\"{Item.ID}\">{Item.Name}</label></div></div>\n";
	
	// Specifies whether we want to open a folder whenever we select an item.
	tfCBTwoTier.prototype.openFolderOnSelect = false;
		
	// Specifies whether we want to render the top-level items before the folders.
	tfCBTwoTier.prototype.renderItemsFirst = false;
	
	//Specifies whether we want to sort folders
	tfCBTwoTier.prototype.sortFolders = true;
	
	//Specifies whether we want to sort items
	tfCBTwoTier.prototype.sortItems = true;

	// Specifies the folder to render. If this property is set to a reference to a folder,
	// the control will render only this folder instead of all folders:
	tfCBTwoTier.prototype.folderToRender = null;

	// The zero-based index or name of the Form which contains the control:
	tfCBTwoTier.prototype.formID = 0;

	// Renders a specified folder:
	tfCBTwoTier.prototype.renderFolder = function( folder, html ) {
		if ( this.folderFormatFunction == null ) {
			var code = "html.push(\"" + this.folderFormatString.replace( /"/g, "\\\"" ).replace( /\n/g, "\\n" ) + "\");";
			code = code.replace( /\{Name\}/g, "\");html.push(\"" + this.name + "\");html.push(\"" );
			code = code.replace( /\{Folder\.ID\}/g, "\");html.push(folder.id);html.push(\"" );
			code = code.replace( /\{Folder\.Name\}/g, "\");html.push(folder.name);html.push(\"" );
			code = code.replace( /\{Folder\.Items\}/g, "\");for(var i=0;i<folder.items.length;i++)this.renderItem(folder.items[i],html);html.push(\"" );
			this.folderFormatFunction = new Function( "folder", "html", code );
		}

		// Do not render empty folders:
		if ( folder.items.length > 0 ) {
			this.folderFormatFunction( folder, html );
		}
	}

	// Renders a specified item:
	tfCBTwoTier.prototype.renderItem = function( item, html ) {
		this.lastItem = item;

		if ( this.itemFormatFunction == null ) {
			var code = "html.push(\"" + this.itemFormatString.replace( /"/g, "\\\"" ).replace( /\n/g, "\\n" ) + "\");";
			code = code.replace( /\{Checkbox}/g, "<input type=\\\"checkbox\\\" name=\\\"{Name}_values\\\" value=\\\"{Item.Value}\\\" id=\\\"{Item.ID}\\\"{Disabled}>" );
			code = code.replace( /\{Name\}/g, "\");html.push(\"" + this.name + "\");html.push(\"" );
			code = code.replace( /\{Item\.ID\}/g, "\");html.push(item.id);html.push(\"" );
			code = code.replace( /\{Item\.Name\}/g, "\");html.push(item.name);html.push(\"" );
			code = code.replace( /\{Item\.Value\}/g, "\");html.push(item.value);html.push(\"" );
			code = code.replace( /\{Disabled\}/g, "\");html.push( this.disabled ? \" disabled='Yes'\" : \"\" );html.push(\"" );
			this.itemFormatFunction = new Function( "item", "html", code );
		}

		this.itemFormatFunction( item, html );
	}
	
	// Returns the regular expression for the text entered by user in QuickFind control:
	tfCBTwoTier.prototype.getSearchExpression = function( text ) {
		if ( text == null || text.length == 0 ) return null;
		if ( text.length < 3 ) return new RegExp( "^(" + text + ")", "ig" );
		return new RegExp( "(" + text + ")", "ig" );
	}
	
	// Gets a list of all items whose name contains the specified text.
	tfCBTwoTier.prototype.find = function( text ) {
		var result = [];
		result.regularExpression = this.getSearchExpression( text );
		
		if ( result.regularExpression != null ) {
			result.names = new Object();
			result.duplicates = new Object();
			
			var callback = function( item, result ) {
				if ( !item.isSelected() && item.isEnabled() && item.name.match( result.regularExpression ) ) {
					result.push( item );
					result.duplicates[ item.name ] = ( result.names[ item.name ] == true );
					result.names[ item.name ] = true;
				}
			}

			this.iterateItems( callback, result );
			
			// The result will be sorted alphabetically by item name.
			// Duplicate items will be sorted by folder name.
			result.sort( function( a, b ) { 
				if ( strCompare_UniCode( a.name, b.name ) ) return 1;
				if ( strCompare_UniCode( b.name, a.name ) ) return -1;

				if ( a.folder != null && b.folder != null ) {
					if ( strCompare_UniCode( a.folder.name, b.folder.name ) ) return 1;
					if ( strCompare_UniCode( b.folder.name, a.folder.name ) ) return -1;
				}

				return 0;
			} );
		}
		
		return result;
	}

	// Opens all folders that contain selected items.
	tfCBTwoTier.prototype.openSelectedFolders = function() {
		this.iterateFolders( function( folder ) { if ( folder.selectedItemsCount > 0 ) folder.open(); } );
	}
	
	tfCBTwoTier.prototype.scrollItemToTop = function(item) {
		var myDiv = xGetElementById(this.divID);	

		// compute the relative offset of the item within the checkboxlist div
		var checkBoxListOffsetTop = getAbsoluteOffsetTop(this.divID);
		var itemOffsetTop = getAbsoluteOffsetTop(item.id);		
		var relativeItemOffset = itemOffsetTop - checkBoxListOffsetTop;

		// scroll the div vertically
		myDiv.scrollTop = relativeItemOffset;
	}
	
	//Set the needed to highlight item selected and prompt for second category alert (MHX)
	tfCBTwoTier.prototype.setFlagItemHighlightAndAlertSecondCatSelected = function(isHighlight, isAlertNeeded) {
	    this.applyAlertSecondCatSelected = isHighlight;
		this.highlightSelectedItem = isAlertNeeded;
	}
/******************************************************************************
 * tfCBTwoTier_Folder
 *****************************************************************************/
 
	// Initializes a new instance of the tfCBTwoTier_Folder class.
	function tfCBTwoTier_Folder( checkBoxListName, id, folder ) {
		this.checkBoxListName = checkBoxListName;
		this.id = id;
		this.name = folder[ 0 ];
		this.items = [];
		for ( var i = 0; i < folder[ 1 ].length; i++ ) if ( folder[ 1 ][ i ] != null ) this.items.push( new tfCBTwoTier_Item( checkBoxListName, this, this.nextItemID(), folder[ 1 ][ i ] ) );
		if ( folder.length > 2 ) this.folderInfo = folder[ 2 ]; 
	}

	tfCBTwoTier_Folder.prototype.nextItemID = function() {
		if ( this.itemCnt == null ) this.itemCnt = 0;
		return this.id + "-" + this.itemCnt++;
	}

	tfCBTwoTier_Folder.prototype.isOpened = function() {
		var element = document.getElementById( this.id + "-open" );
		return ( element != null && element.style.display == "" );
	}

	tfCBTwoTier_Folder.prototype.open = function() {
		tfCBTwoTier_OpenFolder( this.id );
	}

	tfCBTwoTier_Folder.prototype.close = function() {
		tfCBTwoTier_CloseFolder( this.id );
	}

	// Copies the array of folder initializers and returns the copy:
	tfCBTwoTier_Folder.copy = function( folders ) {
		var result = [];

		for ( var i = 0; i < folders.length; i++ ) {
			var newFolder = [ folders[ i ][ 0 ], tfCBTwoTier_Item.copy( folders[ i ][ 1 ] ) ];
			if ( folders[ i ].length > 2 ) newFolder.push( folders[ i ][ 2 ] );
			result.push( newFolder );
		}

		return result;
	}

	// Adds folders from the "foldersToAdd" list to the "existingFolders" list.
	// We assume the folders are ordered by name.
	tfCBTwoTier_Folder.add = function( existingFolders, foldersToAdd ) {
		for ( var i = 0; i < foldersToAdd.length; i++ ) {
			// The name and value of the folder to insert:
			var folderName  = foldersToAdd[ i ][ 0 ];

			// The existing or newly created folder:
			var folder = null;
			
			for ( var j = 0; j < existingFolders.length; j++ ) {
				// If the folder already exists in the folder, then do add items to this folder.
				if ( existingFolders[ j ][ 0 ].toLowerCase() == folderName.toLowerCase() ) {
					folder = existingFolders[ j ];
					tfCBTwoTier_Item.add( existingFolders[ j ][ 1 ], foldersToAdd[ i ][ 1 ] )
					break;
				}
	
				// Otherwise insert the new folder, but make sure to preserve.
				// the alphabetical order of the folder names.
				if ( strCompare_UniCode( existingFolders[ j ][ 0 ], folderName ) ) {
					folder = [ folderName, tfCBTwoTier_Item.copy( foldersToAdd[ i ][ 1 ] ) ];
					existingFolders.splice( j, 0, folder );
					break;
				}
			}
				
			// If we have not found the folder in the "existingFolders" list nor found
			// a right place to insert new folder, then append the new folder
			// at the end of the "existingFolders" list
			if ( folder == null ) {
				var newFolder = [ folderName, tfCBTwoTier_Item.copy( foldersToAdd[ i ][ 1 ] ) ];
				if ( foldersToAdd[ i ].length > 2 ) newFolder.push( foldersToAdd[ i ][ 2 ] );
				existingFolders.push( newFolder );
			}
		}
	}

	// Removes folders from the "foldersToRemove" list from the "existingFolders" list.
	// We assume the folders are ordered by name.
	tfCBTwoTier_Folder.remove = function( existingFolders, foldersToRemove ) {
		for ( var i = 0; i < foldersToRemove.length; i++ ) {
			// The name of the folder to remove:
			var folderName  = foldersToRemove[ i ][ 0 ];
			
			for ( var j = 0; j < existingFolders.length; j++ ) {
				// The folders to remove does not exist in the "existingFolders" list:
				if ( strCompare_UniCode( existingFolders[ j ][ 0 ], folderName ) ) break;
				
				if ( existingFolders[ j ][ 0 ].toLowerCase() == folderName.toLowerCase() ) {
					// Remove items from the folder
					tfCBTwoTier_Item.remove( existingFolders[ j ][ 1 ], foldersToRemove[ i ][ 1 ] );

					// If there are no more items left then remove the folder itself:
					if ( existingFolders[ j ][ 1 ].length == 0 ) existingFolders.splice( j, 1 );
					break;
				}
			}
		}
	}
	
	// Disables all items in the folder:
	tfCBTwoTier_Folder.prototype.disable = function() {
		for ( var i = 0; i < this.items.length; i++ ) {
			if ( !this.items[ i ].isSelected() ) {
				this.items[ i ].disable();
			}
		}
	}
	
	// Enables all items in the folder:
	tfCBTwoTier_Folder.prototype.enable = function() {
		for ( var i = 0; i < this.items.length; i++ ) {
			this.items[ i ].enable();
		}
	}

/******************************************************************************
 * tfCBTwoTier_Item
 *****************************************************************************/
 
	// Initializes a new instance of the tfCBTwoTier_Item class.
	function tfCBTwoTier_Item( checkBoxListName, folder, id, item ) {
		this.checkBoxListName = checkBoxListName;
		this.folder = folder;
		this.id = id;
		this.value = item[ 0 ];
		this.name = item[ 1 ];
		if ( item.length > 2 ) this.itemInfo = item[ 2 ]; 
	}
	
	// Gets the reference to the tfCBTwoTier control which owns the current item:
	tfCBTwoTier_Item.prototype.getCheckBoxList = function() { return window[ this.checkBoxListName ]; }

	// Tests whether the item is selected.
	tfCBTwoTier_Item.prototype.isSelected = function() {
		if ( this.checkBox == null ) return false;
		return this.checkBox.checked;
	}

	// Tests whether the item is enabled.
	tfCBTwoTier_Item.prototype.isEnabled = function() {
		if ( this.checkBox == null ) return false;
		return !this.checkBox.disabled;
	}

	// Tests whether the item is disabled:
	tfCBTwoTier_Item.prototype.isDisabled = function() {
		if ( this.checkBox != null ) return this.checkBox.disabled;
		return false;
	}

	// Enables the item.
	tfCBTwoTier_Item.prototype.enable = function() {
		if ( this.checkBox != null ) this.checkBox.disabled = false;
	}

	// Disables the item.
	tfCBTwoTier_Item.prototype.disable = function() {
		if ( this.checkBox != null ) this.checkBox.disabled = true;
	}

	// Focuses the item.
	tfCBTwoTier_Item.prototype.focus = function() {
		if ( this.checkBox != null ) this.checkBox.focus();
	}

	// Selects the current item.
	tfCBTwoTier_Item.prototype.select = function( value ) {
		if ( this.checkBox != null && value != this.isSelected() ) {
			this.checkBox.checked = value;
			if ( !this.getCheckBoxList().onItemSelected( this ) ) this.checkBox.checked = false;
		}
	}

	// Copies the array of item initializers and returns the copy:
	tfCBTwoTier_Item.copy = function( items ) {
		var result = [];

		for ( var i = 0; i < items.length; i++ ) {
			if (items[i]!= undefined)
			{
				var newItem = [ items[ i ][ 0 ], items[ i ][ 1 ] ];
				if ( items[ i ].length > 2 ) newItem.push( items[ i ][ 2 ] );
				result.push( newItem );
			}
		}

		return result;
	}

	// Adds items from the "itemsToAdd" list to the "existingItems" list.
	// We assume the items are ordered by name.
	tfCBTwoTier_Item.add = function( existingItems, itemsToAdd ) {
		for ( var i = 0; i < itemsToAdd.length; i++ ) {
			// The name and value of the item to insert:
			var itemValue = itemsToAdd[ i ][ 0 ];
			var itemName  = itemsToAdd[ i ][ 1 ];

			// The existing or newly created item:
			var item = null;
			
			for ( var j = 0; j < existingItems.length; j++ ) {
				// If the item already exists in the folder, then do nothing.
				if ( existingItems[ j ][ 1 ].toLowerCase() == itemName.toLowerCase() ) {
					item = existingItems[ j ];
					break;
				}
	
				// Otherwise insert the new item, but make sure to preserve.
				// the alphabetical order of the item names.
				if ( strCompare_UniCode( existingItems[ j ][ 1 ], itemName ) ) {
					item = [ itemValue, itemName ];
					existingItems.splice( j, 0, item );
					break;
				}
			}
				
			// If we have not found the item in the "existingItems" list nor found
			// a right place to insert new item, then append the new item
			// at the end of the "existingItems" list
			if ( item == null ) {
				var newItem = [ itemValue, itemName ];
				if ( itemsToAdd[ i ].length > 2 ) newItem.push( itemsToAdd[ i ][ 2 ] );
				existingItems.push( newItem );
			}
		}
	}

	// Removes items from the "itemsToRemove" list from the "existingItems" list.
	// We assume the items are ordered by name.
	tfCBTwoTier_Item.remove = function( existingItems, itemsToRemove ) {
		for ( var i = 0; i < itemsToRemove.length; i++ ) {
			// The name of the item to remove:
			var itemName  = itemsToRemove[ i ][ 1 ];
			
			for ( var j = 0; j < existingItems.length; j++ ) {
				// The item to remove does not exist in the "existingItems" list:
				if ( existingItems[ j ][ 1 ] > itemName ) break;
				
				// If we found the item, we will remove it from the "existingItems" list.
				if ( existingItems[ j ][ 1 ] == itemName ) {
					existingItems.splice( j, 1 );
					break;
				}
			}
		}
	}

/******************************************************************************
 * tfCBTwoTier_Selection
 *****************************************************************************/
 
	// Initializes a new instance of the tfCBTwoTier_Selection class.
	function tfCBTwoTier_Selection() {
		this.folders = [];
		this.items = [];
		this.itemCount = 0;
	}
	
	// Merges the current selection with another one, ie. adds folders and items that do not exist 
	// in the current selection.
	tfCBTwoTier_Selection.prototype.merge = function( selection ) {
		for ( var i = 0; i < selection.folders.length; i++ ) {
			var folder = null;
			var folder1 = selection.folders[ i ];
			
			for ( var ii = 0; ii < this.folders.length; ii++ ) {
				var folder2 = this.folders[ ii ];

				if ( folder2.name == folder1.name ) {
					folder = folder2;
					break;
				}

				else if ( strCompare_UniCode( folder2.name, folder1.name ) ) {
					folder = new tfCBTwoTier_Folder( folder1.checkBoxListName, folder1.id, [ folder1.name, [], folder1.folderInfo ] );
					this.folders.splice( ii, 0, folder );
					break;
				}
			}

			if ( folder == null ) {
				folder = new tfCBTwoTier_Folder( folder1.checkBoxListName, folder1.id, [ folder1.name, [], folder1.folderInfo ] );
				this.folders.push( folder );
			}

			for ( var j = 0; j < folder1.items.length; j++ ) {
				var item = null;
				var item1 = folder1.items[ j ];

				for ( var jj = 0; jj < folder.items.length; jj++ ) {
					var item2 = folder.items[ jj ];
					
					if ( item2.name == item1.name ) {
						item = item2;
						break;
					}
                                
					else if ( strCompare_UniCode( item2.name, item1.name ) ) {
						item = item1;
						folder.items.splice( jj, 0, item );
						this.itemCount++;
						break;
					}
				}

				if ( item == null ) {
					item = item1;
					folder.items.push( item );
					this.itemCount++;
				}
			}
		}
			
		for ( var j = 0; j < selection.items.length; j++ ) {
			var item = null;
			var item1 = selection.items[ j ];

			for ( var jj = 0; jj < this.items.length; jj++ ) {
				var item2 = this.items[ jj ];
				
				if ( item2.name == item1.name ) {
					item = item2;
					break;
				}
                        
				else if ( strCompare_UniCode( item2.name, item1.name ) ) {
					item = item1;
					this.items.splice( jj, 0, item );
					this.itemCount++;
					break;
				}
			}

			if ( item == null ) {
				item = item1;
				this.items.push( item );
				this.itemCount++;
			}
		}
	}

/******************************************************************************
 * tfCBTwoTier_TallyBox
 *****************************************************************************/
 
	// Initializes a new instance of the tfCBTwoTier_TallyBox class.
	function tfCBTwoTier_TallyBox( divID ) {
		this.checkBoxListNames = [];
		this.divID = divID;
		var cultureInfoStringHiddenField = document.getElementById("hfCultureInfoString");
		if (cultureInfoStringHiddenField != null && cultureInfoStringHiddenField.value != null)
		{
			cultureInfoString = cultureInfoStringHiddenField.value.toLowerCase();
		}
	}
	
	// Defines the layout of the folders within the TallyBox.	
	tfCBTwoTier_TallyBox.prototype.folderFormatString = "<div class=\"folder\">\n<div class=\"header\">{Folder.Name}</div>\n{Folder.Items}</div>";
	
	// Defines the layout of the items within the TallyBox.
	tfCBTwoTier_TallyBox.prototype.itemFormatString = "<div class=\"item\"><div class=\"remove\"><a href=\"javascript:{Name}.select('{Item.Value}',false);\">X</a></div>{Item.Name}</div>\n";
	
	// Gets the reference to the tfCBTwoTier object which owns the current TallyBox:
	tfCBTwoTier_TallyBox.prototype.getCheckBoxList = function( i ) {
		return window[ this.checkBoxListNames[ i ] ];
	}

	// Renders a specified folder:
	tfCBTwoTier_TallyBox.prototype.renderFolder = function( folder, html ) {
		if ( this.folderFormatFunction == null ) {
			var code = "var code=[];code.push(\"" + this.folderFormatString.replace( /"/g, "\\\"" ).replace( /\n/g, "\\n" ) + "\");html.push(code.join(\"\"));";
			code = code.replace( /\{Name\}/g, "\");code.push(folder.checkBoxListName);code.push(\"" );
			code = code.replace( /\{Folder\.Name\}/g, "\");code.push(folder.name);code.push(\"" );
			code = code.replace( /\{Folder\.Items\}/g, "\");for(var i=0;i<folder.items.length;i++)this.renderItem(folder.items[i],code);code.push(\"" );
			this.folderFormatFunction = new Function( "folder", "html", code );
		}

		this.folderFormatFunction( folder, html );
	}

	// Renders a specified item:
	tfCBTwoTier_TallyBox.prototype.renderItem = function( item, html ) {
		if ( this.itemFormatFunction == null ) {
			var code = "var code=[];code.push(\"" + this.itemFormatString.replace( /"/g, "\\\"" ).replace( /\n/g, "\\n" ) + "\");html.push(code.join(\"\"));";
			code = code.replace( /\{Name\}/g, "\");code.push(item.checkBoxListName);code.push(\"" );
			code = code.replace( /\{Item\.Name\}/g, "\");code.push(item.name);code.push(\"" );
			code = code.replace( /\{Item\.Value\}/g, "\");code.push(item.value);code.push(\"" );
			this.itemFormatFunction = new Function( "item", "html", code );
		}

		this.itemFormatFunction( item, html );
	}

	// Gets the selected folders and items:
	tfCBTwoTier_TallyBox.prototype.getSelection = function() {
		if ( this.selection == null ) {
			this.selection = new tfCBTwoTier_Selection()
		
			for ( var i = 0; i < this.checkBoxListNames.length; i++ ) {
				this.selection.merge( this.getCheckBoxList( i ).getSelection() );
			}
		}

		return this.selection;
	}
	

	// Renders the tally box itself.
	tfCBTwoTier_TallyBox.prototype.render = function() {
		var html = [];
		
		for ( var i = 0; i < this.getSelection().folders.length; i++ ) {
			this.renderFolder( this.getSelection().folders[ i ], html );
		}
		
		for ( var i = 0; i < this.getSelection().items.length; i++ ) {
			this.renderItem( this.getSelection().items[ i ], html );
		}

		var element = document.getElementById( this.divID );
		element.innerHTML = html.join( "\n" );
	}
	
	// Attaches event handles to all related controls:
	tfCBTwoTier_TallyBox.prototype.attachEvents = function() {
		var tallyBox = this;
		
		for ( var i = 0; i < this.checkBoxListNames.length; i++ ) {
			this.getCheckBoxList( i ).addChangeEventHandler( function( sender ) { tallyBox.selection = null; tallyBox.render(); } );
		}
	}

/******************************************************************************
 * tfCBTwoTier_QuickFind
 *****************************************************************************/
 
	// Initializes a new instance of the tfCBTwoTier_QuickFind class.
	function tfCBTwoTier_QuickFind( id, checkBoxListName, applyNextGen ) {
		this.id = id;
		this.checkBoxListName = checkBoxListName;
		this.menu = new tfCBTwoTier_QuickFind_Menu( id + ".menu", id + ":Menu" );
	    this.applyNextGen = applyNextGen.toLowerCase();
	}
	
	// Max number of results returned by the find() method;
	tfCBTwoTier_QuickFind.prototype.maxFindResults = 10;

	// Specifies whether we want to focus items selected with the QuickFind control:
	tfCBTwoTier_QuickFind.prototype.focusSelectedItem = true;

	// The default text for the text box
	tfCBTwoTier_QuickFind.prototype.defaultText = "";

	// The text displayed when no item matches the search string:
	tfCBTwoTier_QuickFind.prototype.noMatchText = "";
	
	// Gets the reference to the tfCBTwoTier control which owns the current TallyBox:
	tfCBTwoTier_QuickFind.prototype.getCheckBoxList = function() {	return window[ this.checkBoxListName ]; }
	
	// Gets the reference to the text box containing the name of the selected item.
	tfCBTwoTier_QuickFind.prototype.getTextBox = function() { return window[ "txt" + this.id ]; }
	
	// Gets the reference to the button.
	tfCBTwoTier_QuickFind.prototype.getButton = function() { return window[ "btn" + this.id ]; }
	
	// Gets the reference to the menu containing all matching items.
	tfCBTwoTier_QuickFind.prototype.getMenu = function() { return window[ "mnu" + this.id ]; }

	// Clears the content of the menu and textbox:
	tfCBTwoTier_QuickFind.prototype.clear = function() {
		this.menu.clear();
		this.menu.hide();
		this.getTextBox().value = this.defaultText;
		if(this.applyNextGen == "false")
		    this.getTextBox().className = "TextBox Blur";
	    else
		    this.getTextBox().className = "TextBoxNextGen Blur";
		this.disableButton();
	}

	// Disables the button:
	tfCBTwoTier_QuickFind.prototype.disableButton = function() {
		var btn = this.getButton();

		if ( btn != null && !btn.disabled ) {
			btn.disabled = true;
			btn.className += " Disabled";
		}
	}

	// Enables the button:
	tfCBTwoTier_QuickFind.prototype.enableButton = function() {
		var btn = this.getButton();

		if ( btn != null && btn.disabled ) {
			btn.disabled = false;
			btn.className = btn.className.replace( / Disabled/ig, "" );
		}
	}

	// Attaches event handlers:
	tfCBTwoTier_QuickFind.prototype.attachEvents = function() {
		// Menu.onSelect:
		var code = [];
		code.push( "if(menuItem != null) {");
		code.push( "var item = " + this.checkBoxListName + ".getItem( menuItem.value );" );
		code.push( "item.select( true );" );

		if ( this.focusSelectedItem ) {
			code.push( "if ( item.isSelected() ) {" );
			//code.push( this.checkBoxListName + ".focusLastItem();" )
			code.push( "if ( item.folder != null ) item.folder.open();" );
			code.push( this.checkBoxListName + ".scrollItemToTop(item);" );
			code.push( "}" );
		}
	
		code.push( "}");    // end if(menuItem != null)
	
		code.push( this.id + ".clear();" );
	    code.push( this.id + ".getTextBox().focus();")
		
		this.menu.onSelect = new Function( "menu", "menuItem", code.join( "" ) );

		// Menu.focusChanged:
		code = [];
		code.push("if ( selectedItem != null ) {");
		code.push( "txt" + this.id + ".value = selectedItem.name;" );
		code.push( "btn" + this.id + ".selectedValue = selectedItem.value;" );
		code.push( this.id + ".enableButton();" );
		code.push("}");
		this.menu.focusChanged = new Function( "selectedItem", code.join( "" ) );
				
		// TextBox:
		this.getTextBox().quickFind = this;
		this.getTextBox().onmouseover = function() { this.quickFind.menu.isFocused = true; }
		this.getTextBox().onmouseout  = function() { this.quickFind.menu.isFocused = false; }
		this.getTextBox().onkeyup = function() { if ( !this.navigationMove ) this.quickFind.render(); }
		this.getTextBox().onblur = function() { if ( !this.quickFind.menu.isFocused ) this.quickFind.clear(); }
		
		this.getTextBox().onfocus = function() {
			this.quickFind.render();
			if(this.quickFind.applyNextGen == "false")
			    this.className = "TextBox";
			else
			    this.className = "TextBoxNextGen";
			if ( this.value == this.quickFind.defaultText ) this.value = "";
		}
		
		this.getTextBox().onkeydown = function( e ) {
			if ( e == null ) e = window.event;
			var code = e.keyCode ? e.keyCode : e.which;
			this.navigationMove = false;

			switch ( code ) {
				case 38: {
					if ( this.quickFind.menu.isVisible() ) {
						this.quickFind.menu.moveUp();
					}

					else {
						this.quickFind.show();
					}

					this.navigationMove = true;
					break;
				}
				
				case 40: {
					if ( this.quickFind.menu.isVisible() ) {
						this.quickFind.menu.moveDown();
					}

					else {
						this.quickFind.show();
					}

					this.navigationMove = true;
					break;
				}
                        
				case 27: {
					if ( this.quickFind.menu.isVisible() ) {
						this.quickFind.menu.hide();
						this.navigationMove = true;
					}

					break;
				}
                        
				case 13:
				case 9: {
					if ( this.quickFind.menu.isVisible() ) {
						this.quickFind.menu.select();
						this.navigationMove = true;
					}

					break;
				}	
			}
			
			return !this.navigationMove;
		};

		// Button:
		if ( this.getButton() != null ) {
			this.getButton().disabled = true;
			this.getButton().quickFind = this;
			this.getButton().onmouseover = function() { this.quickFind.menu.isFocused = true; }
			this.getButton().onmouseout  = function() { this.quickFind.menu.isFocused = false; }

			this.getButton().onclick = function() {
				if ( this.disabled ) return;
				var item = this.quickFind.getCheckBoxList().getItem( this.selectedValue );
				item.select( true );
				
				if ( item.isSelected() && this.quickFind.focusSelectedItem ) {
					if ( item.folder != null ) item.folder.open();
					this.quickFind.getCheckBoxList().scrollItemToTop(item);
				}
				
				this.quickFind.clear();
				this.quickFind.getTextBox().focus();
			}
		}
		
		// CheckBoxList:
		var quickFind = this;
		this.getCheckBoxList().addChangeEventHandler( function( sender ) { quickFind.text = null; } );
	}
	
	// Renders the menu with the matching items.	
	tfCBTwoTier_QuickFind.prototype.render = function() {
		if ( this.text != this.getTextBox().value ) {
			this.text = this.getTextBox().value;
			this.disableButton();
			
			if ( this.text != null && this.text.length > 0 && this.text != this.defaultText ) {
				var items = this.getCheckBoxList().find( this.text );
				this.menu.clear();
				
				if ( items.length > 0 ) {
					for ( var i = 0; i < items.length && i < this.maxFindResults; i++ ) {
						var text = items[ i ].name.replace( items.regularExpression, "<b>$1</b>" );
						
						if ( items.duplicates[ items[ i ].name ] == true && items[ i ].folder != null ) {
							text += " (" + items[ i ].folder.name + ")";
						}
                        
						this.menu.addItem( items[ i ].value, items[ i ].name, text );
					}
                
					this.menu.show();
					this.menu.render();
			
					// If the user has entered entire name of an item, then enable the button.
					if ( this.getButton() != null ) {
						if ( items.length == 1 && items[ 0 ].name.toLowerCase() == this.text.toLowerCase() ) {
							this.getButton().selectedValue = items[ 0 ].value;
							this.enableButton();
						}
						
						else {
							this.getButton().selectedValue = "";
							this.disableButton();
						}
					}
				}
                
				else if ( this.noMatchText != null && this.noMatchText.length > 0 ) {
					this.menu.show();
					this.menu.getElement().innerHTML = "<div class=\"NoMatch\">" + this.noMatchText + "</div>";
				}
			}

			else {
				this.menu.hide();
			}
		}
	}

/******************************************************************************
 * tfCBTwoTier_QuickFind_Menu
 *****************************************************************************/

	// Creates a new instance of the tfCBTwoTier_QuickFind_Menu class with a specified id.
	function tfCBTwoTier_QuickFind_Menu( name, id ) {
		this.name = name;
		this.id = id;
		this.clear();
		if ( window[ name ] == null ) window[ name ] = this;
	}
	
	// Clears the item list
	tfCBTwoTier_QuickFind_Menu.prototype.clear = function() {
		this.items = [];
		this.selectedItemIndex = -1;
		this.focusedItemIndex = -1;
		this.getElement().innerHTML = "";
	}
	
	// Adds a new item to the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.addItem = function( value, name, text ) {
		this.items.push( new tfCBTwoTier_QuickFind_MenuItem( value, name, text, this.items.length, this.id ) );
		if ( this.focusedItemIndex = -1 ) this.focusedItemIndex = 0;
	}

	// Gets the HTML element the menu will be rendered to:
	tfCBTwoTier_QuickFind_Menu.prototype.getElement = function() {
		if ( this.element == null ) {
			this.element = document.getElementById( this.id );
		}

		return this.element;
	}

	// Gets the item selected by user:
	tfCBTwoTier_QuickFind_Menu.prototype.getSelectedItem = function() {
		if ( this.selectedItemIndex >= 0 && this.selectedItemIndex < this.items.length ) {
			return this.items[ this.selectedItemIndex ];
		}

		return null;
	}

	// Renders the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.render = function() {
		var html = [];

		for ( var i = 0; i < this.items.length; i++ ) {
			this.items[ i ].render( html );
		}
		
		this.getElement().innerHTML = html.join( "\n" );
		this.attachEvents();
		this.focus( 0 );
	}

	tfCBTwoTier_QuickFind_Menu.prototype.attachEvents = function() {
		this.getElement().onmouseover = new Function( this.name + ".isFocused = true;" );
		this.getElement().onmouseout  = new Function( this.name + ".isFocused = false;" );
		
		for ( var i = 0; i < this.items.length; i++ ) {
			this.items[ i ].getElement().onmouseover = new Function( this.name + ".focus(" + i + ");" + this.name + ".focusChanged(" + this.name + ".items[ " + i + " ] );" );
			this.items[ i ].getElement().onclick     = new Function( this.name + ".select();" );
		}
	}

	// Focuses a specified item:
	tfCBTwoTier_QuickFind_Menu.prototype.focus = function( index ) {
		if ( this.items.length > 0 ) {
			if ( index < 0 ) index = 0;
			if ( index >= this.items.length ) index = this.items.length - 1;
			this.blur( this.focusedItemIndex );
			this.focusedItemIndex = index;
			this.items[ index ].focus();
		}
	}

	// Blurs a specified item:
	tfCBTwoTier_QuickFind_Menu.prototype.blur = function( index ) {
		if ( index >= 0 && index < this.items.length ) {
			this.items[ index ].blur();
		}
	}

	// Hides the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.hide = function() {
		this.getElement().style.display = "none";
	}

	// Shows the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.show = function() {
		this.getElement().style.display = "";
	}

	// Gets the value indicating whether the control is visible or not:
	tfCBTwoTier_QuickFind_Menu.prototype.isVisible = function() {
		return ( this.getElement().style.display != "none" );
	}

	// Focuses the previous item in the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.moveUp = function() {
		this.focus( this.focusedItemIndex - 1 );
		this.focusChanged( this.items[ this.focusedItemIndex ] );
	}

	// Focuses the next item in the menu:
	tfCBTwoTier_QuickFind_Menu.prototype.moveDown = function() {
		this.focus( this.focusedItemIndex + 1 );
		this.focusChanged( this.items[ this.focusedItemIndex ] );
	}

	// Additional actions executed when the focus has been changed:
	tfCBTwoTier_QuickFind_Menu.prototype.focusChanged = function( selectedItem ) {
	}

	// Selects a specified item:
	tfCBTwoTier_QuickFind_Menu.prototype.select = function() {
		this.selectedItemIndex = this.focusedItemIndex;

		if ( this.onSelect != null ) {
			this.onSelect( this, this.getSelectedItem() );
		}

		this.hide();
	}

/******************************************************************************
 * tfCBTwoTier_QuickFind_MenuItem
 *****************************************************************************/

	// Creates a new instance of the tfCBTwoTier_QuickFind_MenuItem class with a specified value and display text.
	function tfCBTwoTier_QuickFind_MenuItem( value, name, text, index, parentID ) {
		this.value = value;
		this.name = name;
		this.text = text;
		this.index = index;
		this.parentID = parentID;
	}
	
	// Gets the ID of the div the item is rendered to:
	tfCBTwoTier_QuickFind_MenuItem.prototype.getID = function() {
		return this.parentID + ":" + this.index;
	}
	
	// Gets the element associated with the item:
	tfCBTwoTier_QuickFind_MenuItem.prototype.getElement = function() {
		if ( this.element == null ) {
			this.element = document.getElementById( this.getID() );
		}
		
		return this.element;
	}

	// Renders the item:
	tfCBTwoTier_QuickFind_MenuItem.prototype.render = function( html ) {
		html.push( "<div class=\"Item\" id=\"" + this.getID() + "\">" + this.text + "</div>" );
	}
	
	// Focuses a specified item:
	tfCBTwoTier_QuickFind_MenuItem.prototype.focus = function() {
		this.getElement().className = "Item MouseOver";
	}
	
	// Blurs a specified item:
	tfCBTwoTier_QuickFind_MenuItem.prototype.blur = function() {
		this.getElement().className = "Item";
	}

/******************************************************************************
 * tfCBTwoTier_SelectionValidator
 *****************************************************************************/
 
	// This validator checks whether the user did not select more items or folders than allowed.
	function tfCBTwoTier_SelectionValidator( errorMessage, maxItems, maxFolders, maxItemsInFolder ) {
		this.errorMessage = errorMessage;
		this.maxItems = maxItems != null ? maxItems : 0;
		this.maxFolders = maxFolders != null ? maxFolders : 0;
		this.maxItemsInFolder = maxItemsInFolder != null ? maxItemsInFolder : 0;
	}
	
	// Tests whether the user has not exceeded the total number of selected items:
	tfCBTwoTier_SelectionValidator.prototype.validate = function( checkBoxList ) {
		var result = true;
		var selection = checkBoxList.getSelection();
		
		if ( this.maxItems > 0 ) result = result && ( selection.itemCount <= this.maxItems );
		if ( result && this.maxFolders > 0 ) result = result && ( selection.folders.length <= this.maxFolders );
		
		if ( result && this.maxItemsInFolder > 0 ) {
			for ( var i = 0; i < selection.folders.length; i++ ) {
				result = result && ( selection.folders[ i ].items.length <= this.maxItemsInFolder );
				if ( !result ) break;
			}
		}
		
		return result;
	}

	// Disables unselected items if the user has reached maximum number of selected items:
	tfCBTwoTier_SelectionValidator.prototype.execute = function( checkBoxList, item ) {
		var changed = false;
		checkBoxList.getSelection();
		
		if ( item == null || item.isSelected() ) {
			if ( item == null ) {
				checkBoxList.iterateItems( function( item ) { if ( item.isDisabled() ) item.enable(); } );
			}

			if ( this.maxItems > 0 && checkBoxList.getSelection().itemCount >= this.maxItems ) {
				checkBoxList.iterateItems( function( item ) { if ( !item.isSelected() ) item.disable(); } );
				changed = true;
			}

			if ( this.maxFolders > 0 && checkBoxList.getSelection().folders.length >= this.maxFolders ) {
				checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount == 0 ) folder.disable(); } );
				changed = true;
			}
			
			if ( this.maxItemsInFolder > 0 ) {
				if ( item == null ) {
					checkBoxList.iterateFolders( new Function( "folder", "if ( folder.selectedItemsCount >= " + this.maxItemsInFolder + " ) folder.disable();" ) );
					changed = true;
				}
				
				else if ( item.folder != null && item.folder.selectedItemsCount >= this.maxItemsInFolder ) {
					item.folder.disable();
					changed = true;
				}
			}
			
			if ( changed ) this.onDisable();
		}

		else {
			if ( item.folder != null && this.maxItemsInFolder > 0 && item.folder.selectedItemsCount == ( this.maxItemsInFolder - 1 ) ) {
				item.folder.enable();
				changed = true;
			}

			if ( item.folder != null && this.maxFolders > 0 && item.folder.selectedItemsCount == 0 && checkBoxList.getSelection().folders.length == ( this.maxFolders - 1 ) ) {
				if ( this.maxItemsInFolder == 0 ) {
					checkBoxList.iterateFolders( function( folder ) { folder.enable(); } );
				}

				else {
					var maxItemsInFolder = this.maxItemsInFolder;
					checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount < maxItemsInFolder ) folder.enable(); } );
				}

				changed = true;
			}
			
			if ( this.maxItems > 0 && checkBoxList.getSelection().itemCount == ( this.maxItems - 1 ) ) {
				changed = true;
				checkBoxList.iterateItems( function( item ) { if ( item.folder == null ) item.enable(); } );
				
				if ( this.maxFolders == 0 && this.maxItemsInFolder == 0 ) {
					checkBoxList.iterateFolders( function( folder ) { folder.enable(); } );
				}

				else if ( this.maxFolders == 0 ) {
					var maxItemsInFolder = this.maxItemsInFolder;
					checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount < maxItemsInFolder ) folder.enable(); } );
				}

				else if ( this.maxItemsInFolder == 0 ) {
					if ( checkBoxList.getSelection().folders.length < this.maxFolders ) {
						checkBoxList.iterateFolders( function( folder ) { folder.enable(); } );
					}

					else {
						checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount > 0 ) folder.enable(); } );
					}
				}

				else {
					var maxItemsInFolder = this.maxItemsInFolder;

					if ( checkBoxList.getSelection().folders.length < this.maxFolders ) {
						checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount < maxItemsInFolder ) folder.enable(); } );
					}

					else {
						checkBoxList.iterateFolders( function( folder ) { if ( folder.selectedItemsCount > 0 && folder.selectedItemsCount < maxItemsInFolder ) folder.enable(); } );
					}
				}
			}
			
			if ( changed ) this.onEnable();
		}
	}
	
	// Executed when the checkboxlist disables the checkboxes:
	tfCBTwoTier_SelectionValidator.prototype.onDisable = function() {}
	
	// Executed when the checkboxlist enables the checkboxes:
	tfCBTwoTier_SelectionValidator.prototype.onEnable = function() {}
	
 /******************************************************************************
 * tfCBTwoTier_ClearAll
 *****************************************************************************/
 
    //tfCBTwoTier_ClearAll functions
    function tfCBTwoTier_ClearAll(checkBoxListName ) {
        this.checkBoxListName = checkBoxListName;
	    if (getCheckBoxList() != null)
	    {
	        getCheckBoxList().clearSelection();
	    }
    }
	
    function getCheckBoxList() { return window[this.checkBoxListName]; }

